﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Xml;

namespace ajayumi.Platform.Core.Security
{
    /// <summary>
    /// RSA加密类
    /// </summary>
    public class RSACryptoProvider : AsymmetricCryptoProvider
    {
        #region Variables
        private const string XML_PUBLIC_KEY = "PublicKey.xml";
        private const string XML_PRIVATE_KEY = "PrivateKey.xml";

        private BigInteger D = null;
        private BigInteger Exponent = null;
        private BigInteger Modulus = null;

        #endregion

        public RSACryptoProvider()
            : base("RSACryptoProvider")
        {
        }

        protected override void Init()
        {
            if (this.AymAlg == null)
            { this.AymAlg = new RSACryptoServiceProvider(); }

            if (string.IsNullOrEmpty(this.XmlPublicKey))
            {
                this.XmlPublicKey = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, XML_PUBLIC_KEY);
            }
            if (string.IsNullOrEmpty(this.XmlPrivateKey))
            {
                this.XmlPrivateKey = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, XML_PRIVATE_KEY);
            }
        }

        /// <summary>
        /// 公钥加密
        /// </summary>
        /// <param name="plainStr"></param>
        /// <returns></returns>
        public override string Encrypt(string plainStr)
        {
            return this.PublicKeyEncrypt(this.XmlPublicKey, plainStr);
        }

        /// <summary>
        /// 私钥解密
        /// </summary>
        /// <param name="encryptedStr"></param>
        /// <returns></returns>
        public override string Decrypt(string encryptedStr)
        {
            return this.PrivateKeyDecrypt(this.XmlPrivateKey, encryptedStr);
        }

        public override string EncryptByPrivateKey(string plainStr)
        {
            return this.PrivateKeyEncrypt(this.XmlPrivateKey, plainStr);
        }

        public override string DecryptByPublicKey(string encryptedStr)
        {
            return this.PublicKeyDecrypt(this.XmlPublicKey, encryptedStr);
        }

        /// <summary>
        /// 公钥加密   直接使用.NET提供的RSA加密算法
        /// </summary>
        /// <param name="publicKeyPath"></param>
        /// <param name="strMsg"></param>
        /// <returns></returns>
        protected string PublicKeyEncrypt(string publicKeyPath, string strMsg)
        {
            XmlDocument myDoc = new XmlDocument();
            myDoc.Load(publicKeyPath);
            this.AymAlg.FromXmlString(myDoc.OuterXml);
            byte[] bytes = Encoding.UTF8.GetBytes(strMsg);
            return Convert.ToBase64String((this.AymAlg as RSACryptoServiceProvider).Encrypt(bytes, false));
        }


        /// <summary>
        /// 私钥解密  直接使用.NET提供的RSA解密算法 
        /// </summary>
        /// <param name="privateKeyPath"></param>
        /// <param name="decryptMsg"></param>
        /// <returns></returns>
        protected string PrivateKeyDecrypt(string privateKeyPath, string decryptMsg)
        {
            XmlDocument myDoc = new XmlDocument();
            myDoc.Load(privateKeyPath);
            this.AymAlg.FromXmlString(myDoc.OuterXml);
            byte[] rgb = Convert.FromBase64String(decryptMsg);
            byte[] bytes = (this.AymAlg as RSACryptoServiceProvider).Decrypt(rgb, false);
            return Encoding.UTF8.GetString(bytes);
        }


        //私钥加密   
        protected string PrivateKeyEncrypt(string privateKeyPath, string strMsg)
        {
            XmlDocument myDoc = new XmlDocument();
            myDoc.Load(privateKeyPath);
            this.AymAlg.FromXmlString(myDoc.OuterXml);
            RSAParameters rsaParams = (this.AymAlg as RSACryptoServiceProvider).ExportParameters(true);

            D = new BigInteger(rsaParams.D);
            Modulus = new BigInteger(rsaParams.Modulus);
            Exponent = new BigInteger(rsaParams.Exponent);

            byte[] message = Encoding.UTF8.GetBytes(strMsg);
            byte[] encMessage = null;

            BigInteger bnData = new BigInteger(message);
            BigInteger encData = bnData.modPow(D, Modulus);
            encMessage = encData.getBytes();
            return Convert.ToBase64String(encMessage);
        }

        //公钥解密   
        protected string PublicKeyDecrypt(string publicKeyPath, string decryptMsg)
        {
            XmlDocument myDoc = new XmlDocument();
            myDoc.Load(publicKeyPath);
            this.AymAlg.FromXmlString(myDoc.OuterXml);

            RSAParameters rsaParams = (this.AymAlg as RSACryptoServiceProvider).ExportParameters(false);

            Modulus = new BigInteger(rsaParams.Modulus);
            Exponent = new BigInteger(rsaParams.Exponent);

            byte[] decMessage = Convert.FromBase64String(decryptMsg);
            byte[] message = null;

            BigInteger encData = new BigInteger(decMessage);
            BigInteger bnData = encData.modPow(Exponent, Modulus);
            message = bnData.getBytes();
            return Encoding.UTF8.GetString(message);
        }

        public override byte[] Sign(string plainStr)
        {
            if (!File.Exists(this.XmlPrivateKey))
            {
                throw new FileNotFoundException("签名密钥文件不存在。");
            }

            try
            {
                byte[] dataToSign = Encoding.Unicode.GetBytes(plainStr);
                byte[] signedData;

                XmlDocument myDoc = new XmlDocument();
                myDoc.Load(this.XmlPrivateKey);
                this.AymAlg.FromXmlString(myDoc.OuterXml);

                signedData = (this.AymAlg as RSACryptoServiceProvider).SignData(dataToSign, new SHA1CryptoServiceProvider());

                return signedData;
            }
            catch (System.Exception ex)
            {
                throw new Exception("签名失败！\r\n" + ex.Message, ex);
            }
        }

        public override bool VerifySign(string plainStr, byte[] signedData)
        {
            if (!File.Exists(this.XmlPublicKey))
            {
                throw new Exception("验证密钥文件不存在。");
            }

            try
            {
                byte[] dataToSign = Encoding.Unicode.GetBytes(plainStr);

                bool result = false;

                XmlDocument myDoc = new XmlDocument();
                myDoc.Load(this.XmlPublicKey);
                this.AymAlg.FromXmlString(myDoc.OuterXml);

                result = (this.AymAlg as RSACryptoServiceProvider).VerifyData(dataToSign, new SHA1CryptoServiceProvider(), signedData);

                return result;
            }
            catch (System.Exception ex)
            {
                throw new Exception("验证签名失败！\r\n" + ex.Message, ex);
            }
        }

        /// <summary>
        /// 生成公、私钥
        /// </summary>
        public void KeyGenerator()
        {
            //XmlDocument privateKey = new XmlDocument();
            //XmlDocument publicKey = new XmlDocument();

            ////  use RSA convenience methods to stuff public and private keys into dom's
            //privateKey.LoadXml(this.AymAlg.ToXmlString(true));
            //publicKey.LoadXml(this.AymAlg.ToXmlString(false));

            //privateKey.Save(this.XmlPrivateKey);
            //publicKey.Save(this.XmlPublicKey);
            this.KeyGenerator(this.XmlPublicKey, this.XmlPrivateKey);
        }

        /// <summary>
        /// 生成公、私钥
        /// </summary>
        /// <param name="pubKeyPath">公钥保存路径</param>
        /// <param name="priKeyPath">私钥保存路径</param>
        public void KeyGenerator(string pubKeyPath, string priKeyPath)
        {
            XmlDocument privateKey = new XmlDocument();
            XmlDocument publicKey = new XmlDocument();

            //  use RSA convenience methods to stuff public and private keys into dom's
            privateKey.LoadXml(this.AymAlg.ToXmlString(true));
            publicKey.LoadXml(this.AymAlg.ToXmlString(false));

            privateKey.Save(priKeyPath);
            publicKey.Save(pubKeyPath);
        }


        public override string SignToStr(string plainStr)
        {
            byte[] data = this.Sign(plainStr);
            //return UTF8Encoding.BigEndianUnicode.GetString(data);
            return Convert.ToBase64String(data);
        }

        public override bool VerifySignFormStr(string plainStr, string signedStr)
        {
            byte[] data = Convert.FromBase64String(signedStr);
            //byte[] data = UTF8Encoding.BigEndianUnicode.GetBytes(signedStr);
            return this.VerifySign(plainStr, data);
        }
    }
}
